@/***************************************************************************
@ * Copyright (c) 2024 Microsoft Corporation 
@ * 
@ * This program and the accompanying materials are made available under the
@ * terms of the MIT License which is available at
@ * https://opensource.org/licenses/MIT.
@ * 
@ * SPDX-License-Identifier: MIT
@ **************************************************************************/
@
@
@/**************************************************************************/
@/**************************************************************************/
@/**                                                                       */
@/** ThreadX Component                                                     */
@/**                                                                       */
@/**   Thread                                                              */
@/**                                                                       */
@/**************************************************************************/
@/**************************************************************************/
@
#ifdef TX_INCLUDE_USER_DEFINE_FILE
#include "tx_user.h"
#endif
@
    .global  _tx_thread_current_ptr
    .global  _tx_thread_execute_ptr
    .global  _tx_timer_time_slice
    .global  _tx_thread_system_stack_ptr
#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))     
    .global _tx_execution_thread_enter
    .global _tx_execution_thread_exit
#endif
#ifdef TX_LOW_POWER
    .global tx_low_power_enter
    .global tx_low_power_exit
#endif
@
@
    .text
    .align 4
    .syntax unified
@/**************************************************************************/
@/*                                                                        */
@/*  FUNCTION                                               RELEASE        */
@/*                                                                        */
@/*    _tx_thread_schedule                               Cortex-M0/AC6     */
@/*                                                           6.2.1        */
@/*  AUTHOR                                                                */
@/*                                                                        */
@/*    William E. Lamie, Microsoft Corporation                             */
@/*                                                                        */
@/*  DESCRIPTION                                                           */
@/*                                                                        */
@/*    This function waits for a thread control block pointer to appear in */
@/*    the _tx_thread_execute_ptr variable.  Once a thread pointer appears */
@/*    in the variable, the corresponding thread is resumed.               */
@/*                                                                        */
@/*  INPUT                                                                 */
@/*                                                                        */
@/*    None                                                                */
@/*                                                                        */
@/*  OUTPUT                                                                */
@/*                                                                        */
@/*    None                                                                */
@/*                                                                        */
@/*  CALLS                                                                 */
@/*                                                                        */
@/*    None                                                                */
@/*                                                                        */
@/*  CALLED BY                                                             */
@/*                                                                        */
@/*    _tx_initialize_kernel_enter          ThreadX entry function         */
@/*    _tx_thread_system_return             Return to system from thread   */
@/*    _tx_thread_context_restore           Restore thread's context       */
@/*                                                                        */
@/*  RELEASE HISTORY                                                       */
@/*                                                                        */
@/*    DATE              NAME                      DESCRIPTION             */
@/*                                                                        */
@/*  09-30-2020     William E. Lamie        Initial Version 6.1            */
@/*  03-02-2021     Scott Larson            Modified comment(s), add       */
@/*                                           low power code,              */
@/*                                           resulting in version 6.1.5   */
@/*  03-08-2023      Scott Larson            Include tx_user.h,            */
@/*                                            resulting in version 6.2.1  */
@/*                                                                        */
@/**************************************************************************/
@VOID   _tx_thread_schedule(VOID)
@{
    .global  _tx_thread_schedule
    .thumb_func
_tx_thread_schedule:
@
@    /* This function should only ever be called on Cortex-M0
@       from the first schedule request. Subsequent scheduling occurs
@       from the PendSV handling routines below. */
@
@    /* Clear the preempt-disable flag to enable rescheduling after initialization on Cortex-M targets.  */
@
    MOVS    r0, #0                                  @ Build value for TX_FALSE
    LDR     r2, =_tx_thread_preempt_disable         @ Build address of preempt disable flag
    STR     r0, [r2, #0]                            @ Clear preempt disable flag
@
@    /* Enable interrupts */
@
    CPSIE   i
@
@    /* Enter the scheduler for the first time.  */
@
    LDR     r0, =0x10000000                         @ Load PENDSVSET bit
    LDR     r1, =0xE000ED04                         @ Load NVIC base
    STR     r0, [r1]                                @ Set PENDSVBIT in ICSR
    DSB                                             @ Complete all memory accesses
    ISB                                             @ Flush pipeline
@
@    /* Wait here for the PendSV to take place.  */
@
__tx_wait_here:
    B       __tx_wait_here                          @ Wait for the PendSV to happen
@}
@
@    /* Generic context switch-out switch-in handler...  Note that this handler is
@       common for both PendSV and SVCall.  */
@
    .global PendSV_Handler
.thumb_func
.thumb_func
PendSV_Handler:
    .global  __tx_PendSVHandler
    .global  __tx_SVCallHandler
    .thumb_func
__tx_PendSVHandler:
    .thumb_func
__tx_SVCallHandler:
@
@    /* Get current thread value and new thread pointer.  */
@
    .thumb_func
__tx_ts_handler:

#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))     
@
@    /* Call the thread exit function to indicate the thread is no longer executing.  */
@
    CPSID   i                                       @ Disable interrupts
    PUSH    {r0, lr}                                @ Save LR (and r0 just for alignment)
    BL  _tx_execution_thread_exit                   @ Call the thread exit function
    POP     {r0, r1}                                @ Recover LR
    MOV     lr, r1                                  @
    CPSIE   i                                       @ Enable interrupts
#endif
    LDR     r0, =_tx_thread_current_ptr             @ Build current thread pointer address
    LDR     r2, =_tx_thread_execute_ptr             @ Build execute thread pointer address
    MOVS    r3, #0                                  @ Build NULL value
    LDR     r1, [r0]                                @ Pickup current thread pointer
@
@    /* Determine if there is a current thread to finish preserving.  */
@
    CMP     r1,#0                                   @ If NULL, skip preservation
    BEQ     __tx_ts_new                             @
@
@    /* Recover PSP and preserve current thread context.  */
@
    STR     r3, [r0]                                @ Set _tx_thread_current_ptr to NULL
    MRS     r3, PSP                                 @ Pickup PSP pointer (thread's stack pointer)
    SUBS    r3, r3, #16                             @ Allocate stack space
    STM     r3!, {r4-r7}                            @ Save its remaining registers (M3 Instruction: STMDB r12!, {r4-r11})
    MOV     r4,r8                                   @
    MOV     r5,r9                                   @
    MOV     r6,r10                                  @
    MOV     r7,r11                                  @
    SUBS    r3, r3, #32                             @ Allocate stack space
    STM     r3!,{r4-r7}                             @
    SUBS    r3, r3, #20                             @ Allocate stack space
    MOV     r5, lr                                  @ Move LR into R4
    STR     r5, [r3]                                @ Save LR
    STR     r3, [r1, #8]                            @ Save its stack pointer
@
@    /* Determine if time-slice is active. If it isn't, skip time handling processing.  */
@
    LDR     r4, =_tx_timer_time_slice               @ Build address of time-slice variable
    LDR     r5, [r4]                                @ Pickup current time-slice
    CMP     r5, #0                                  @ If not active, skip processing
    BEQ     __tx_ts_new                             @
@
@    /* Time-slice is active, save the current thread's time-slice and clear the global time-slice variable.  */
@
    STR     r5, [r1, #24]                           @ Save current time-slice
@
@    /* Clear the global time-slice.  */
@
    MOVS    r5, #0                                  @ Build clear value
    STR     r5, [r4]                                @ Clear time-slice
@
@    /* Executing thread is now completely preserved!!!  */
@
__tx_ts_new:
@
@    /* Now we are looking for a new thread to execute!  */
@
    CPSID   i                                       @ Disable interrupts
    LDR     r1, [r2]                                @ Is there another thread ready to execute?
    CMP     r1, #0                                  @
    BEQ     __tx_ts_wait                            @ No, skip to the wait processing
@
@    /* Yes, another thread is ready for else, make the current thread the new thread.  */
@
    STR     r1, [r0]                                @ Setup the current thread pointer to the new thread
    CPSIE   i                                       @ Enable interrupts
@
@    /* Increment the thread run count.  */
@
__tx_ts_restore:
    LDR     r7, [r1, #4]                            @ Pickup the current thread run count
    LDR     r4, =_tx_timer_time_slice               @ Build address of time-slice variable
    LDR     r5, [r1, #24]                           @ Pickup thread's current time-slice
    ADDS    r7, r7, #1                              @ Increment the thread run count
    STR     r7, [r1, #4]                            @ Store the new run count
@
@    /* Setup global time-slice with thread's current time-slice.  */
@
    STR     r5, [r4]                                @ Setup global time-slice

#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE))           
@
@    /* Call the thread entry function to indicate the thread is executing.  */
@
    PUSH    {r0, r1}                                @ Save r0/r1
    BL      _tx_execution_thread_enter              @ Call the thread execution enter function
    POP     {r0, r1}                                @ Recover r3
#endif
@
@    /* Restore the thread context and PSP.  */
@
    LDR     r3, [r1, #8]                            @ Pickup thread's stack pointer
    LDR     r5, [r3]                                @ Recover saved LR
    ADDS    r3, r3, #4                              @ Position past LR
    MOV     lr, r5                                  @ Restore LR
    LDM     r3!,{r4-r7}                             @ Recover thread's registers (r4-r11)
    MOV     r11,r7                                  @
    MOV     r10,r6                                  @
    MOV     r9,r5                                   @
    MOV     r8,r4                                   @
    LDM     r3!,{r4-r7}                             @
    MSR     PSP, r3                                 @ Setup the thread's stack pointer
@
@    /* Return to thread.  */
@
    BX      lr                                      @ Return to thread!
@
@    /* The following is the idle wait processing... in this case, no threads are ready for execution and the
@       system will simply be idle until an interrupt occurs that makes a thread ready. Note that interrupts
@       are disabled to allow use of WFI for waiting for a thread to arrive.  */
@
__tx_ts_wait:
    CPSID   i                                       @ Disable interrupts
    LDR     r1, [r2]                                @ Pickup the next thread to execute pointer
    STR     r1, [r0]                                @ Store it in the current pointer
    CMP     r1, #0                                  @ If non-NULL, a new thread is ready!
    BNE     __tx_ts_ready                           @

#ifdef TX_LOW_POWER
    PUSH    {r0-r3}
    BL      tx_low_power_enter                      @ Possibly enter low power mode
    POP     {r0-r3}
#endif

#ifdef TX_ENABLE_WFI
    DSB                                             @ Ensure no outstanding memory transactions
    WFI                                             @ Wait for interrupt
    ISB                                             @ Ensure pipeline is flushed
#endif

#ifdef TX_LOW_POWER
    PUSH    {r0-r3}
    BL      tx_low_power_exit                       @ Exit low power mode
    POP     {r0-r3}
#endif

    CPSIE   i                                       @ Enable interrupts
    B       __tx_ts_wait                            @ Loop to continue waiting
@
@    /* At this point, we have a new thread ready to go. Clear any newly pended PendSV - since we are
@       already in the handler!  */
@
__tx_ts_ready:
    LDR     r7, =0x08000000                         @ Build clear PendSV value
    LDR     r5, =0xE000ED04                         @ Build base NVIC address
    STR     r7, [r5]                                @ Clear any PendSV
@
@    /* Re-enable interrupts and restore new thread.  */
@
    CPSIE   i                                       @ Enable interrupts
    B       __tx_ts_restore                         @ Restore the thread
